﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using EntryEngine.Serialize;
using System.IO;
using System.Linq;
using System.Text;

namespace EntryEngine.Network
{
    /// <summary>标记此特性代表接口为前后端交互协议</summary>
    [AttributeUsage(AttributeTargets.Interface)]
    public class ProtocolStubAttribute : Attribute
    {
        private byte protocol;
        private Type callback;
        private string agentType;
        public byte Protocol { get { return protocol; } }
        public Type Callback { get { return callback; } }
        public string AgentType { get { return agentType; } }
        public ProtocolStubAttribute(byte protocol, Type callback)
        {
            this.protocol = protocol;
            this.callback = callback;
        }
        public ProtocolStubAttribute(byte protocol, Type callback, string agentType)
        {
            this.protocol = protocol;
            this.callback = callback;
            this.agentType = agentType;
        }
    }

    // socket
    public class LinkSocket : LinkBinary, IConnector
    {
        public Socket Socket
        {
            get;
            private set;
        }
        public override bool IsConnected
        {
            get
            {
                if (Socket != null && Socket.Connected)
                {
                    if (Socket.Poll(0, SelectMode.SelectRead) && Socket.Available == 0)
                    {
                        return false;
                    }
                    return true;
                }
                return false;
            }
        }
        public override bool CanRead
        {
            get { return Socket != null && Socket.Available >= MAX_BUFFER_SIZE; }
        }
        protected override int DataLength
        {
            get { return Socket.Available; }
        }
        public override IPEndPoint EndPoint
        {
            get { return Socket == null ? null : (IPEndPoint)Socket.RemoteEndPoint; }
        }

        public LinkSocket()
        {
        }
        public LinkSocket(Socket socket)
        {
            if (socket == null)
                throw new ArgumentNullException("socket");
            this.Socket = socket;
            socket.SendBufferSize = MaxBuffer;
            socket.ReceiveBufferSize = MaxBuffer;
        }

        protected override int PeekSize(byte[] buffer, out int peek)
        {
            peek = 0;
            return Socket.Receive(buffer, 0, MAX_BUFFER_SIZE, SocketFlags.Peek);
        }
        protected override int InternalRead(byte[] buffer, int offset, int size)
        {
            SocketError error;
            int read = Socket.Receive(buffer, offset, size, SocketFlags.None, out error);
            if (error != SocketError.Success)
                throw new SocketException((int)error);
            return read;
        }
        protected override void InternalFlush(byte[] buffer)
        {
            SocketError error;
            Socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, out error, null, null);
            //Socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, out error, SendCallback, buffer);
            if (error != SocketError.Success)
                throw new SocketException((int)error);
        }
        //private void SendCallback(IAsyncResult ar)
        //{
        //    SocketError error;
        //    int sent = Socket.EndSend(ar, out error);
        //}
        public override void Close()
        {
            base.Close();
            if (Socket != null)
            {
                if (Socket.Connected)
                {
                    Socket.Shutdown(SocketShutdown.Both);
                }
                Socket.Close();
                Socket = null;
            }
        }
        protected virtual Socket BuildSocket()
        {
            throw new NotImplementedException();
        }
        protected void BuildConnection()
        {
            if (IsConnected)
                throw new InvalidOperationException("try connect while socket has been connected.");
            Close();
            Socket = BuildSocket();
            Socket.SendBufferSize = MaxBuffer;
            Socket.ReceiveBufferSize = MaxBuffer;
        }
        public void ConnectSync(string host, ushort port)
        {
            BuildConnection();
            Socket.Connect(host, port);
        }
        public virtual AsyncData<Link> Connect(string host, ushort port)
        {
            BuildConnection();
            AsyncData<Link> async = new AsyncData<Link>();
            async.Run();
            Socket.BeginConnect(host, port, ar =>
            {
                try
                {
                    Socket.EndConnect(ar);
                    if (Socket.Connected)
                        async.SetData(this);
                    else
                        async.Cancel();
                }
                catch (Exception ex)
                {
                    async.Error(ex);
                }
            }, Socket);
            return async;
        }
    }
	public class LinkTcp : LinkSocket
	{
		public LinkTcp()
		{
		}
        public LinkTcp(Socket socket) : base(socket)
		{
		}

        protected override Socket BuildSocket()
        {
            return new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

		public static LinkTcp Connect(string host, ushort port, Action<Socket> onConnect)
		{
			Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			if (onConnect != null)
				onConnect(socket);
			socket.Connect(host, port);
			return new LinkTcp(socket);
		}
        public static AsyncData<LinkTcp> ConnectAsync(string host, ushort port, Action<Socket> onConnect)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            if (onConnect != null)
                onConnect(socket);
            AsyncData<LinkTcp> async = new AsyncData<LinkTcp>();
            socket.BeginConnect(host, port,
                e =>
                {
                    try
                    {
                        socket.EndConnect(e);
                        async.SetData(new LinkTcp(socket));
                    }
                    catch (Exception ex)
                    {
                        async.Error(ex);
                    }
                }, socket);
            return async;
        }
    }
    [Code(ECode.ToBeContinue | ECode.MayBeReform)]
    public class LinkUdp : LinkSocket
    {
        private IPEndPoint endPoint;
       
        public override IPEndPoint EndPoint
        {
            get { return endPoint == null ? base.EndPoint : endPoint; }
        }
        public override bool IsConnected
        {
            get
            {
                return endPoint != null || base.IsConnected;
            }
        }

        public LinkUdp()
		{
		}
        //public LinkUdp(Socket socket) : base(socket)
        //{
        //}
        public LinkUdp(Socket socket, IPEndPoint endPoint) : base(socket)
        {
            if (endPoint == null)
                throw new ArgumentNullException("endPoint");
            this.endPoint = endPoint;
            //Connect(endPoint.Address.ToString(), (ushort)endPoint.Port);
        }

        public void Bind(string ip, ushort port)
        {
            Bind(new IPEndPoint(IPAddress.Parse(ip), port));
        }
        public void Bind(IPEndPoint ep)
        {
            if (ep == null)
                throw new ArgumentNullException("ep");
            if (Socket != null)
                throw new InvalidOperationException("can't bind because socket is not null");
            BuildConnection();
            Socket.Bind(ep);
        }
        protected override int InternalRead(byte[] buffer, int offset, int size)
        {
            EndPoint ep = new IPEndPoint(0, 0);
            int read = Socket.ReceiveFrom(buffer, SocketFlags.None, ref ep);
            return read;
        }
        protected override int PeekSize(byte[] buffer, out int peek)
        {
            EndPoint ep = new IPEndPoint(0, 0);
            peek = 0;
            peek = Socket.ReceiveFrom(buffer, SocketFlags.Peek, ref ep);
            return peek;
        }
        protected override Socket BuildSocket()
        {
            return new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }
        public override AsyncData<Link> Connect(string host, ushort port)
        {
            BuildConnection();
            endPoint = new IPEndPoint(IPAddress.Parse(host), port);
            var sync = new AsyncData<Link>();
            sync.Run();
            sync.SetData(this);
            return sync;
        }
        protected override void InternalFlush(byte[] buffer)
        {
            if (endPoint == null)
                base.InternalFlush(buffer);
            else
                Socket.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, endPoint, null, null);
        }
    }

}
